-
Notifications
You must be signed in to change notification settings - Fork 575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[unitaryHACK] Add reset error to noise channels #1321
Conversation
Hi @nahumsa! Thanks for this PR -- this is a great issue to tackle. Just a bit of housekeeping before we can provide a review:
Let us know when these have been addressed, and the PR is ready for review! Alternatively, feel free to ask any questions. |
…into add-reset-error
Thanks for the response, @josh146 ! I will fix those issues and let you know when it's ready for a review. |
I think that this is ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this great PR @nahumsa! In particular, fantastic tests.
However, it doesn't appear that the tests are passing; they are giving the error
AssertionError: Gradient recipe must have one entry for each parameter!
The grad_recipe
you have defined provides a gradient recipe only for the first parameter:
grad_recipe = ([[1, 0, 1], [-1, 0, 0]],)
You will need to also provide the gradient recipe for the second parameter, p_1
. Let me know if you have any questions!
pennylane/ops/channel.py
Outdated
@classmethod | ||
def _kraus_matrices(cls, *params): | ||
p_0, p_1 = params[0], params[1] | ||
K0 = np.sqrt(1 - p_0 - p_1) * np.eye(2) | ||
K1 = np.sqrt(p_0) * np.array([[1, 0], [0, 0]]) | ||
K2 = np.sqrt(p_1) * np.array([[0, 0], [0, 1]]) | ||
return [K0, K1, K2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
tests/ops/test_channel_ops.py
Outdated
@pytest.mark.parametrize("angle", np.linspace(0, 2 * np.pi, 7)) | ||
def test_grad_reset(self, angle, tol): | ||
"""Test that analytical gradient is computed correctly for different states. Channel | ||
grad recipes are independent of channel parameter""" | ||
|
||
dev = qml.device("default.mixed", wires=1) | ||
p_0, p_1 = 0.5, 0.5 | ||
|
||
@qml.qnode(dev) | ||
def circuit(p_0, p_1): | ||
qml.RX(angle, wires=0) | ||
qml.ResetError(p_0, p_1, wires=0) | ||
return qml.expval(qml.PauliZ(0)) | ||
|
||
gradient = np.squeeze(qml.grad(circuit)(p_0, p_1)) | ||
assert gradient == circuit(1) - circuit(0) | ||
assert np.allclose(gradient, (-2 * np.cos(angle))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great test @nahumsa! Did you calculate the expected gradient value, -2 cos(angle)
, by hand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure about the analytical gradients for this one, I was using TestBitFlip
as a reference.
Maybe could you clarify a bit more about the gradient of this channel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the best sources for learning more about the analytic gradients of noisy channels is https://johannesjakobmeyer.com/blog/004-noisy-parameter-shift/, or alternatively, the PennyLane demonstration: https://pennylane.ai/qml/demos/tutorial_noisy_circuit_optimization.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this particular case, I think @ixfoduap calculated the analytic value by hand, so hopefully he will be able to shed some light!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the followup. I'll read those references.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nahumsa, this is a great PR!
I'm not sure about the analytical gradients. The way we calculate analytical gradients is using this formula, which also appears in the blog post that Josh linked to:
It is valid when the channel can be expressed as a linear combination of channels. This typically means that the Kraus matrices themselves should be a valid channel, which in practice often boils down to the Kraus matrices being unitaries.
In your case, the projectors |0><0|
and |1><1
| are not by themselves valid channels, so it's not accurate that the reset channel can be expressed as a linear combination of channels. This means that the analytical formula should not apply here.
I believe that the reason the tests are passing is because you are setting both p_0
and p_1
equal to each other, which effectively makes this the identity channel --> sqrt(p) |0><0| + sqrt(p) |1><1| = sqrt(p) (|0><0|+|1><1|) = sqrt(p) \identity
and we end up with the gradient of the gate in the circuit.
I would expect the test to fail if you make p_0 != p_1
and compute the gradient by hand there. It's worth checking that this is the case. In any case, the gradient test should be checking gradients for many values of p_0
and p_1
.
If the analytical gradients do not apply here, it is not a big deal; it just means you should change the gradient method to "F" (finite difference) like it is done for other channels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ixfoduap! Thanks for the clarification. After reading the reference, I think that it is not possible to get analytic gradient by the formulas used on other channels.
Thanks for the catch about the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ixfoduap!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nahumsa let us know when you have the tests passing, or if you need a hand anywhere :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and don't forget to add your name to the Contributors section of the changelog! |
Co-authored-by: Josh Izaac <josh146@gmail.com>
Sure! :) |
tests/ops/test_channel_ops.py
Outdated
@pytest.mark.parametrize("angle", np.linspace(0, 2 * np.pi, 7)) | ||
def test_grad_reset(self, angle, tol): | ||
"""Test that analytical gradient is computed correctly for different states. Channel | ||
grad recipes are independent of channel parameter""" | ||
|
||
dev = qml.device("default.mixed", wires=1) | ||
p_0, p_1 = 0.5, 0.5 | ||
|
||
@qml.qnode(dev) | ||
def circuit(p_0, p_1): | ||
qml.RX(angle, wires=0) | ||
qml.ResetError(p_0, p_1, wires=0) | ||
return qml.expval(qml.PauliZ(0)) | ||
|
||
gradient = np.squeeze(qml.grad(circuit)(p_0, p_1)) | ||
assert gradient == circuit(1) - circuit(0) | ||
assert np.allclose(gradient, (-2 * np.cos(angle))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @nahumsa, this is a great PR!
I'm not sure about the analytical gradients. The way we calculate analytical gradients is using this formula, which also appears in the blog post that Josh linked to:
It is valid when the channel can be expressed as a linear combination of channels. This typically means that the Kraus matrices themselves should be a valid channel, which in practice often boils down to the Kraus matrices being unitaries.
In your case, the projectors |0><0|
and |1><1
| are not by themselves valid channels, so it's not accurate that the reset channel can be expressed as a linear combination of channels. This means that the analytical formula should not apply here.
I believe that the reason the tests are passing is because you are setting both p_0
and p_1
equal to each other, which effectively makes this the identity channel --> sqrt(p) |0><0| + sqrt(p) |1><1| = sqrt(p) (|0><0|+|1><1|) = sqrt(p) \identity
and we end up with the gradient of the gate in the circuit.
I would expect the test to fail if you make p_0 != p_1
and compute the gradient by hand there. It's worth checking that this is the case. In any case, the gradient test should be checking gradients for many values of p_0
and p_1
.
If the analytical gradients do not apply here, it is not a big deal; it just means you should change the gradient method to "F" (finite difference) like it is done for other channels.
Codecov Report
@@ Coverage Diff @@
## master #1321 +/- ##
==========================================
- Coverage 99.10% 98.16% -0.95%
==========================================
Files 141 145 +4
Lines 10575 11093 +518
==========================================
+ Hits 10480 10889 +409
- Misses 95 204 +109
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
\end{bmatrix} | ||
|
||
.. math:: | ||
K_2 = \sqrt{p_0}\begin{bmatrix} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nahumsa Why did you change the definition of the channel?
Before approving, could you please point me to a reference where this channel is defined? I would like to make sure that the name "reset error" coincides with the standard usage. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ixfoduap ! I was reading through the qiskit's docs and found a direct reference about the ResetError
Kraus operators:
This was the main reason that I changed the definition of the channel, do you think that this is reasonable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! Thanks for the clarification!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Context: Add Reset Error to noise channels
Description of the Change: Add Single-qubit Reset error channel to pennylane.
Benefits: Add a new noise model that simulates the reset of qubits to 0 or 1 given a probability.
Possible Drawbacks:
Related GitHub Issues: #971